Restful风格的Retrofit2网络请求器

Restful风格的Retrofit2网络请求器

简介

Retrofit2 以其简易的接口配置、强大的扩展支持、优雅的代码结构受到大家的追捧。Retrofit2 的 API 接口定义极具 Restful 风格,且很好的与okhttp3相互结合形成一个高效简洁的网络请求库。

1
2
3
4
5
//通过接口定义你的api
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
1
2
3
4
5
6
//再由Retrofit生成 `GitHubService`接口相对应的方法
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();

GitHubService service = retrofit.create(GitHubService.class);
1
2
//通过GitHubService生成的方法进行网络请求
Call<List<Repo>> repos = service.listRepos("octocat");

详情请见Retrofit2官方教程

原理

Retrofit2库源码中最让人眼前一亮的设计是通过java的动态代理将接口中的每个方法和注解转化成一个单独的ServiceMethod,之后在ServiceMethod中查找合适的CallAdapterFactoryConverterFactory对数据进行处理。

  1. Java的注解和动态代理原理
  2. CallAdapter及其Factory设计思路
  3. Converter及其Factory设计思路

以上亦是整个库的核心原理。

解析

预先准备

1
2
3
4
5
6
7
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

GitHubService service = retrofit.create(GitHubService.class);

整个流程由Retrofit的build开始,将一些必须的参数如baseUrl,CallAdapterFactory,ConverterFactory传入到Retrofit中,之后通过create方法对接口文件中的方法建立动态代理,使得每当方法被调用时都能走到代理的方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();

@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

调用

上面已经说过,当service中的方法被调用时会走入之前的动态代理方法中,那看看该代理方法中做了哪些事情。
首先通过 ServiceMethodCache 查看是否有该方法的缓存,如果没有则通过ServiceMethod的Builder传入retrofit构建出来。

ServiceMethod 的builder构建方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public ServiceMethod build() {
// 查找 合适的 calladapter
callAdapter = createCallAdapter();

// 查找合适的结果转化器
responseType = callAdapter.responseType();
responseConverter = createResponseConverter();

// 编译api方法上的注解,并转化为相应http请求信息,如get,post,header
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}

if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}

if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}

// 获取方法中的参数数据
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
if (Utils.hasUnresolvableType(parameterType)) {
throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
parameterType);
}

Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}

parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}

if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}

return new ServiceMethod<>(this);
}

CallAdapterFactory和CallAdapter

再说CallAddapterFactory前,先说说这个是干什么用的。简而言之就是将Retrofit原生默认的call这个请求对象转换成开发者想要的对象,比如说RxJava中Observable之类。

在动态代理方法中,可以看到有这么一行代码,这里的OkHttpCall是对于OkHttp的一个封装,也就是说我们通过Retrofit进行网络请求实质就是通过OkHttp来进行请求。

1
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

随之其后的是 callAdapter的adapt方法

1
return serviceMethod.callAdapter.adapt(okHttpCall);

serviceMethod 中的 callAdapter 则是在 Retrofit 的 adapterFactories 中查找而得(之前在Retrofit的构建过程中添加addCallAdapterFactory()

1
2
3
4
5
6
7
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}

查找规则则是通过在 CallAdapterFactory 的get方法中判断returnType来进行,一旦查找成功后就会返回相应的CallAdapter。

比如说在 Retrofit2 自带的 DefaultCallAdapterFactory 则是判断 getRawType(returnType) == Call.class,若是则返回原生的Call对象

Rxjava2CallAdapterFactory 中是判断 returnType 是否等于 CompletableFlowableSingleMaybe或者Observable,是则返回Rxjava2CallAdapter.

最后在 CallAdapter 的 adapter方法中可以将传入的call对象转换成自己想要的对象。

ConverterFactory和Converter

Converter 是负责对请求数据和返回数据进行转换的功能,设计思路与 CallAdapterFactory 相似。在 ConverterFactory中有两个比较重要的方法,一个是 requestBodyConverter,这个方法也是通过判断传入的 Type 类型来决定对请求数据的 RequestBodyConverter器,要求返回Converter<?, RequestBody>类型。
另一个是responseBodyConverter,要求返回Converter<ResponseBody, ?>类型。

RequestBodyConverter一般会在 @Body,@Part以及@PartMap的参数解析中使用,用来将请求的数据转换到标准的RequestBody格式。

比如说在 @Part 注解下的参数解析中,由于参数的类型为RequestBody,则ConverterFactory 会优先返回Retrofit自带的 BuiltInConverters 中的 RequestBodyConverter,负责转换RequestBody。

而在 @Body 注解下的参数解析中,则有可能会进入 GsonConverterFactoryGsonRequestBodyConverter中,负责将 @Body声明的实体转换成json字符创。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// BuiltInConverters下的requestBodyConverter方法
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
return RequestBodyConverter.INSTANCE;
}
return null;
}

// GsonConverterFactory 下的 requestBodyConverter 方法
// 来者不拒。。
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonRequestBodyConverter<>(gson, adapter);
}

ResponseBodyConverter则是在 okHttpCall 访问网络结束后调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 // OkHttpCall类中的方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
//...省略部分代码
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}

// ServiceMethod类中的方法
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}

其中 serviceMethod.toResponse()即会调用之前通过ConverterFactory得到的responseConverter.convert方法,将ResponseBody转化为自己想要的数据类型。
这里要注意一点,在之前说过的CallAdapter中有一个responseType()方法,此方法返回的数据类型要与responseConverter.convert(body)的返回类型保持一致。

结语

Retrofit 本身是通过 OkHttp3 实现的网络请求,首先通过动态代理的方式为api service 中的每一个方法一一对应的生成了ServiceMethod;同时通过提供自定义CallAdapterFactory的方式能够将默认的Call请求方法转换成开发者自己想要的数据对象;不知如此,还通过自定义ConverterFactory的方式能够自由的进行请求数据与RequestBody,ResponseBody与返回数据的数据转换。

通过以上所介绍,Retrofit2比较重要的几个原理部分都进行了简单说明。
Retrofit2实现了自身强大的功能与扩展性,能够方便的实现开发者自己想要的功能,同时其美丽而又简洁的代码也是我们借鉴的一个目标。


参考资料:

  1. Retrofit2官方文档
  2. 深入浅出 Retrofit
文章作者: cpacm
文章链接: http://www.cpacm.net/2018/05/22/Android进阶之路 —— Restful风格的Retrofit2网络请求器/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 cpacm
打赏
  • 微信
  • 支付宝

评论